#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("mengminggo");
MODULE_DESCRIPTION("A simple character device driver");

#define MYDEV_NAME "DMMG rwmodule"
#define BUFFER_SIZE 1024

static char buffer[BUFFER_SIZE];
static int buffer_len = 0;

static dev_t mydev_num;
static struct cdev *mydev_cdev;

static int mydev_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "%s: Device opened.\n", MYDEV_NAME);
    return 0;
}

static int mydev_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "%s: Device closed.\n", MYDEV_NAME);
    return 0;
}

static ssize_t mydev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
    int len = count;
    if (len > buffer_len - *offset)
        len = buffer_len - *offset;
    if (copy_to_user(buf, buffer + *offset, len))
        return -EFAULT;
    *offset += len;
    return len;
}

static ssize_t mydev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
    int len = count;
    if (len > BUFFER_SIZE - *offset)
        len = BUFFER_SIZE - *offset;
    if (copy_from_user(buffer + *offset, buf, len))
        return -EFAULT;
    *offset += len;
    if (*offset > buffer_len)
        buffer_len = *offset;
    return len;
}

static const struct file_operations mydev_fops = {
    .owner = THIS_MODULE,
    .read = mydev_read,
    .write = mydev_write,
    .open = mydev_open,
    .release = mydev_release,
};

static int __init mydev_init(void)
{
    int ret;
    printk(KERN_INFO "%s: Initializing.\n", MYDEV_NAME);

    // 分配设备号
    ret = alloc_chrdev_region(&mydev_num, 0, 1, MYDEV_NAME);
    if (ret < 0) {
        printk(KERN_ERR "%s: Failed to allocate device number.\n", MYDEV_NAME);
        return ret;
    }

    // 创建 cdev 结构体
    mydev_cdev = cdev_alloc();
    if (!mydev_cdev) {
        printk(KERN_ERR "%s: Failed to allocate cdev.\n", MYDEV_NAME);
        unregister_chrdev_region(mydev_num, 1);
        return -ENOMEM;
    }
    mydev_cdev->ops = &mydev_fops;

    // 注册字符设备
    ret = cdev_add(mydev_cdev, mydev_num, 1);
    if (ret < 0) {
        printk(KERN_ERR "%s: Failed to register character device.\n", MYDEV_NAME);
        cdev_del(mydev_cdev);
        unregister_chrdev_region(mydev_num, 1);
        return ret;
    }

    printk(KERN_INFO "%s: Initialized successfully.\n", MYDEV_NAME);
    return 0;
}

static void __exit mydev_exit(void)
{
    printk(KERN_INFO "%s: Exiting.\n", MYDEV_NAME);

    // 销毁字符设备
    cdev_del(mydev_cdev);
    unregister_chrdev_region(mydev_num, 1);
}

module_init(mydev_init);
module_exit(mydev_exit);

